package com.ymm.system.core.facade;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ymm.system.api.constants.CargoEntity;
import com.ymm.system.api.dto.CargoDTO;
import com.ymm.system.api.dto.DriverInfoDTO;
import com.ymm.system.api.facade.SystemMatchService;
import com.ymm.system.api.utils.Result;
import com.ymm.system.api.utils.ResultCodeEnum;
import com.ymm.system.core.config.DistanceConfig;
import com.ymm.system.dao.mapper.CargoMapper;
import com.ymm.system.dao.mapper.LltorderMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.apache.ibatis.annotations.Param;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @auther ZhangHongyuan
 * @date 2022/8/3 13:51
 */

@Component
@Slf4j
@Service(version = "server")
public class SystemMatchServiceImpl implements SystemMatchService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    CargoMapper cargoMapper;

    @Resource
    LltorderMapper lltorderMapper;

    private static final double SEARCH_DISTANCE = 10000;

    @Override
//    @Async("asyncExecutor")
    public Result<List<CargoDTO>> driverSearchDayCargoService(Double driverLat, Double driverLon, Integer driverCarType) {
        List<CargoEntity> allCargos = cargoMapper.getAllCargos();

        // 创建列表用于存放符合要求的货源
        List<CargoDTO> giveDriverCargoList = new ArrayList<>();

        int cargoLen = allCargos.size();
        if (cargoLen < 1) {
            // 如果取到的货源为空
            return Result.success(new ArrayList<>());
        }

        for (int i = 0; i < cargoLen; i++) {
            // 获取货源经纬度
            Double cargoSrcLat = allCargos.get(i).getCargoSrcLat();
            Double cargoSrcLon = allCargos.get(i).getCargoSrcLon();
            Double cargoDesLat = allCargos.get(i).getCargoDesLat();
            Double cargoDesLon = allCargos.get(i).getCargoDesLon();
            //计算订单距离
            double orderDistance = algorithm(cargoSrcLon, cargoSrcLat, cargoDesLon, cargoDesLat);

            // 获取货源需要的车型
            Integer cargoRequireType = allCargos.get(i).getCargoRequireType();

            double algorithm = algorithm(driverLat, driverLon, cargoSrcLat, cargoSrcLon);


            // 判断距离是否小于10公里，并且车型相同,此时不设置空驶比例
            if (algorithm <= DistanceConfig.getEMPTY_DISTANCE_FIRST() && cargoRequireType <= driverCarType) {
                CargoDTO cargoDTO = new CargoDTO();
                BeanUtils.copyProperties(allCargos.get(i), cargoDTO);
                cargoDTO.setCargoEmptyDistance(algorithm);
                giveDriverCargoList.add(cargoDTO);
            }
        }
        int temp = giveDriverCargoList.size();

        //小于3个货源，考虑扩宽范围进行兜底
        if (temp < 3) {
            for (int k = 0; k < cargoLen; k++) {
                if (!giveDriverCargoList.contains(allCargos.get(k))) {
                    // 获取货源经纬度
                    Double cargoSrcLat = allCargos.get(k).getCargoSrcLat();
                    Double cargoSrcLon = allCargos.get(k).getCargoSrcLon();
                    Double cargoDesLat = allCargos.get(k).getCargoDesLat();
                    Double cargoDesLon = allCargos.get(k).getCargoDesLon();
                    //计算订单距离
                    double orderDistance2 = algorithm(cargoSrcLon, cargoSrcLat, cargoDesLon, cargoDesLat);

                    // 获取货源需要的车型
                    Integer cargoRequireType = allCargos.get(k).getCargoRequireType();

                    //空驶距离扩宽至20公里
                    double algorithm2 = algorithm(driverLat, driverLon, cargoSrcLat, cargoSrcLon);

                    //空驶比例1.0
                    double emptyDriveRate2 = algorithm2 / orderDistance2;
                    if (algorithm2 < DistanceConfig.getEMPTY_DISTANCE_SECOND() &&
                            cargoRequireType <= driverCarType && emptyDriveRate2 < DistanceConfig.getEMPTY_DRIVE_RATIO_SECOND()) {
                        CargoDTO cargoDTO = new CargoDTO();
                        BeanUtils.copyProperties(allCargos.get(k), cargoDTO);
                        cargoDTO.setCargoEmptyDistance(algorithm2);
                        giveDriverCargoList.add(cargoDTO);
                        log.info("扩大搜索范围" + giveDriverCargoList.size());
                    }
                }
            }
        }
        return Result.success(giveDriverCargoList);

    }

    /**
     * 返回指定时间指定范围内的货源信息
     *
     * @param driverLat
     * @param driverLon
     * @param driverCarType
     * @param deliveryTimeEarliest
     * @param deliveryTimeLatest
     * @return
     */
    @Override
    public Result<List<CargoDTO>> driverSearchSpecifyTimeCargoService(Double driverLat, Double driverLon, Integer driverCarType,
                                                                      Date deliveryTimeEarliest, Date deliveryTimeLatest) {
        if (driverLat == null || driverLon == null || driverCarType == null ||
                deliveryTimeEarliest == null || deliveryTimeLatest == null) {
            log.info("输入请求参数错误");
            return Result.fail(null, ResultCodeEnum.PARAM_ERROR);
        }
        double latitudeDelta = getLatitudeDelta(SEARCH_DISTANCE);
        double longitudeDelta = getLongitudeDelta(driverLat, SEARCH_DISTANCE);
        List<CargoEntity> cargoEntities = cargoMapper.listCargoByDeliveryTimeAndLocation2(deliveryTimeEarliest, deliveryTimeLatest,
                driverLon, driverLat, longitudeDelta, latitudeDelta);
//        List<CargoEntity> cargoEntities = cargoMapper.listCargoByDeliveryTimeAndLocation(deliveryTimeEarliest, deliveryTimeLatest,
//                driverLon, driverLat);
        int cargoLen = cargoEntities.size();
        System.out.println("cargoLen " + cargoLen);
        if (cargoEntities.size() < 1) {
            // 如果取到的货源为空
            log.info("暂无合适订单");
            return Result.success(new ArrayList<>());
        }

        List<CargoDTO> giveDriverCargoList = new ArrayList<>();
        for (int i = 0; i < cargoLen; i++) {
            // 获取货源经纬度
            Double cargoSrcLat = cargoEntities.get(i).getCargoSrcLat().doubleValue();
            Double cargoSrcLon = cargoEntities.get(i).getCargoSrcLon().doubleValue();

            // 获取货源需要的车型
            Integer cargoRequireType = cargoEntities.get(i).getCargoRequireType();

            double algorithm = algorithm(driverLat, driverLon, cargoSrcLat, cargoSrcLon);

            // 判断距离是否小于10公里，并且车型相同
            if (algorithm <= 10000 && cargoRequireType <= driverCarType) {
                CargoDTO cargoDTO = new CargoDTO();
                BeanUtils.copyProperties(cargoEntities.get(i), cargoDTO);
                cargoDTO.setCargoEmptyDistance(algorithm);
                giveDriverCargoList.add(cargoDTO);
            }
        }
        if (giveDriverCargoList.size() < 1) {
            log.info("暂无合适订单");
            return Result.success(new ArrayList<>());
        }
        return Result.success(giveDriverCargoList);
    }

    /**
     * 判断时间是否冲突
     *
     * @param driverOrder
     * @param currentCargo
     * @return
     * @author zikang.yao
     */
    public List<CargoEntity> availableCargo(List<Date> driverOrder, List<CargoEntity> currentCargo) {
        //该司机没有待进行/进行中订单
        if (driverOrder.size() == 0) {
            return currentCargo;
        }
        boolean flag = true;
        long nm = 1000 * 60;//算分钟
        List<CargoEntity> availableCargo = new ArrayList<>();
        for (int i = 0; i < currentCargo.size(); i++) {
            for (int j = 0; j < driverOrder.size(); j++) {
                long driverOrderTime = driverOrder.get(j).getTime();
                long currentCargoTime = currentCargo.get(i).getCargoDeliveryTime().getTime();
                long diffMinutes = Math.abs(driverOrderTime - currentCargoTime) / nm;
                if (diffMinutes < 100) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                availableCargo.add(currentCargo.get(i));
            }
            flag = true;
        }
        return availableCargo;
    }

    /**
     * 派单逻辑
     *
     * @param driverLat
     * @param driverLon
     * @param driverCarType
     * @return
     */
    @Override
//    @Async("asyncExecutor")
    public Result<List<CargoDTO>> PushLongTimeCargo(Double driverLat, Double driverLon, Integer driverCarType, Long driverId) {
        if (Objects.isNull(driverId)) {
            return Result.fail(null, ResultCodeEnum.PARAM_ERROR.getCode(), ResultCodeEnum.PARAM_ERROR.getMessage());
        }

        double latitudeDelta = getLatitudeDelta(SEARCH_DISTANCE);
        double longitudeDelta = getLongitudeDelta(driverLat, SEARCH_DISTANCE);
        List<CargoEntity> cargoEntities = cargoMapper.listOutTimeCargoByLocation2(driverLon, driverLat, longitudeDelta, latitudeDelta, 30);
//        List<CargoEntity> cargoEntities = cargoMapper.listOutTimeCargoByLocation(driverLon, driverLat, 30);
        if (cargoEntities.size() < 1) {
            //搜索五次，每次扩大一次时间范围，最多检索十五分钟以上的订单
            for (int count = 1; count < 6; count++) {
                cargoEntities = cargoMapper.listOutTimeCargoByLocation2(driverLon, driverLat, longitudeDelta, latitudeDelta, 30 - 3 * count);
//                cargoEntities = cargoMapper.listOutTimeCargoByLocation(driverLon, driverLat, 30 - 3 * count);
                if (cargoEntities.size() > 0) {
                    break;
                }
            }
        }
        if (cargoEntities.size() < 1) {
            log.info("暂无合适订单");
            return Result.success();
        }

        List<Date> onShelfOrderDate = lltorderMapper.getOnShelfOrderDate(driverId);
        List<CargoDTO> giveDriverCargoList = new ArrayList<>();
        //从时间上进行一个判断是否可推单
        List<CargoEntity> availableCargo = availableCargo(onShelfOrderDate, cargoEntities);

        //距离记录
        HashMap<CargoEntity, Double> distanceMap = new HashMap<>();
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        //判断该司机是否正在被派单
        String pushingDriver = valueOperations.get("pushingDriver" + driverId.toString());

        if (pushingDriver != null) {
            CargoEntity cargoEntity = cargoMapper.queryCargoByCargoId(Long.valueOf(pushingDriver));
            CargoDTO cargoDTO = new CargoDTO();
            BeanUtils.copyProperties(cargoEntity, cargoDTO);
            Double emptyDistance = algorithm(driverLon, driverLat, cargoEntity.getCargoSrcLon(), cargoEntity.getCargoSrcLat());
            cargoDTO.setCargoEmptyDistance(emptyDistance);
            giveDriverCargoList.add(cargoDTO);
            return Result.success(giveDriverCargoList);
        }

        for (int i = 0; i < availableCargo.size(); i++) {
            // 获取货源经纬度
            Double cargoSrcLat = availableCargo.get(i).getCargoSrcLat();
            Double cargoSrcLon = availableCargo.get(i).getCargoSrcLon();
            // 获取货源需要的车型
            Integer cargoRequireType = availableCargo.get(i).getCargoRequireType();
            //距离
            double distance = algorithm(driverLat, driverLon, cargoSrcLat, cargoSrcLon);
            //判断该单是否被推给过这个司机
            String cargoidPushed = hashOperations.get("cargoache" + availableCargo.get(i).getCargoId().toString(), driverId.toString());
            //判断该单是否正在被推送中
            String cargoPushing = valueOperations.get("pushingCargo" + availableCargo.get(i).getCargoId());
            if (cargoidPushed != null || cargoPushing != null) {
                continue;
            } else {
                // 判断距离是否小于10公里，并且车型相同
                if (distance <= DistanceConfig.getEMPTY_DISTANCE_FIRST() && cargoRequireType <= driverCarType) {
                    //距离记录的map用来后续排序
                    distanceMap.put(availableCargo.get(i), distance);
                }
            }
        }

        if (distanceMap.size() < 1) {
            log.info("暂无合适订单");
            return Result.success();
        } else {
            //按照距离排序选出最短距离的进行派单
            ArrayList<Map.Entry<CargoEntity, Double>> distanceList = new ArrayList<>(distanceMap.entrySet());
            Collections.sort(distanceList, (o1, o2) -> {
                if (o1.getValue() != null && o2.getValue() != null && o2.getValue().compareTo(o1.getValue()) < 0) {
                    return 1;
                } else {
                    return -1;
                }
            });
//            giveDriverCargoList.add(distanceList.get(0).getKey());
            for (int i = 0; i < distanceList.size(); i++) {
                if (valueOperations.setIfAbsent("pushingCargo" + distanceList.get(i).getKey().getCargoId().toString(), driverId.toString(), 60, TimeUnit.SECONDS)) {
                    valueOperations.set("pushingDriver" + driverId.toString(), distanceList.get(i).getKey().getCargoId().toString(), 60, TimeUnit.SECONDS);
                    hashOperations.put("cargoache" + distanceList.get(i).getKey().getCargoId().toString(), driverId.toString(), "1");
                    CargoDTO cargoDTO = new CargoDTO();
                    BeanUtils.copyProperties(distanceList.get(i).getKey(), cargoDTO);
                    cargoDTO.setCargoEmptyDistance(distanceMap.get(distanceList.get(i).getKey()));
                    giveDriverCargoList.add(cargoDTO);
                    return Result.success(giveDriverCargoList);
                }
            }
        }
        return Result.fail(null, ResultCodeEnum.FAIL.getCode(), "没有合适的订单，去你奶奶的");
    }

    public static double algorithm(double longitude1, double latitude1, double longitude2, double latitude2) {
        double Lat1 = rad(latitude1); // 纬度

        double Lat2 = rad(latitude2);

        double a = Lat1 - Lat2;//两点纬度之差

        double b = rad(longitude1) - rad(longitude2); //经度之差

        double s = 2 * Math.asin(Math

                .sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(Lat1) * Math.cos(Lat2) * Math.pow(Math.sin(b / 2), 2)));//计算两点距离的公式

        s = s * 6378137.0;//弧长乘地球半径（半径为米）

        s = Math.round(s * 10000) / 10000;//精确距离的数值

        return s;

    }

    private double getLongitudeDelta(double latitude, double distance) {
        double Lat = rad(latitude);
        return Math.acos(1 - 4 * Math.pow(distance / (2 * 6378137.0), 2) / (Math.cos(2 * Lat) + 1)) * 180 / Math.PI;
    }

    private double getLatitudeDelta(double distance) {
        return distance / 6378137.0 * 180 / Math.PI;
    }

    private static double rad(double d) {
        return d * Math.PI / 180.00; //角度转换成弧度
    }
}
